package com.supermap.fabricsdk;

import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.exception.ProposalException;

import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;

/**
 * @author liqs
 * @version 1.0
 * @date 2020/7/31 16:10
 */
public class SmChaincodeImp implements SmChaincode {

    Logger logger = Logger.getLogger(SmChaincodeImp.class.getName());
    private HFClient hfClient;
    private Channel channel;

    public SmChaincodeImp(HFClient hfClient, Channel channel) {
        this.hfClient = hfClient;
        this.channel = channel;
    }



    @Override
    public LifecycleChaincodePackage createLifecycleChaincodePackage(
            String chaincodeLabel,
            TransactionRequest.Type chaincodeType,
            Path chaincodeSourceLocation,
            String chaincodePath,
            Path metadadataSource)
    {
        LifecycleChaincodePackage lifecycleChaincodePackage = null;
        try {
            lifecycleChaincodePackage = LifecycleChaincodePackage.fromSource(chaincodeLabel, chaincodeSourceLocation, chaincodeType, chaincodePath, metadadataSource);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidArgumentException e) {
            e.printStackTrace();
        }
        return lifecycleChaincodePackage;
    }

    @Override
    public String lifecycleInstallChaincode(LifecycleChaincodePackage lifecycleChaincodePackage) {

        LifecycleInstallChaincodeRequest lifecycleInstallChaincodeRequest = hfClient.newLifecycleInstallChaincodeRequest();
        String packageID = null;
        try {
            lifecycleInstallChaincodeRequest.setLifecycleChaincodePackage(lifecycleChaincodePackage);
            lifecycleInstallChaincodeRequest.setProposalWaitTime(360000);
            Collection<LifecycleInstallChaincodeProposalResponse> responses
                    = hfClient.sendLifecycleInstallChaincodeRequest(lifecycleInstallChaincodeRequest, channel.getPeers());
            Collection<ProposalResponse> successful = new LinkedList<>();
            Collection<ProposalResponse> failed = new LinkedList<>();
            for (LifecycleInstallChaincodeProposalResponse response : responses) {
                if (response.getStatus() == ProposalResponse.Status.SUCCESS) {
                    successful.add(response);
                    if (packageID == null) {
                        packageID = response.getPackageId();
                    } else {
                        logger.info("Miss match on what the peers returned back as the packageID");
                    }
                } else {
                    failed.add(response);
                }
            }

            if (failed.size() > 0) {
                ProposalResponse first = failed.iterator().next();
                logger.info("Not enough endorsers for install :" + successful.size() + ".  " + first.getMessage());
            }
        } catch (InvalidArgumentException e) {
            e.printStackTrace();
        } catch (ProposalException e) {
            e.printStackTrace();
        }
        return packageID;
    }


    @Override
    public CompletableFuture<BlockEvent.TransactionEvent> lifecycleApproveChaincodeDefinitionForMyOrg(
            long sequence,
            String chaincodeName,
            String chaincodeVersion,
            LifecycleChaincodeEndorsementPolicy chaincodeEndorsementPolicy,
            ChaincodeCollectionConfiguration chaincodeCollectionConfiguration,
            boolean initRequired,
            String ChaincodePackageID)
    {

        CompletableFuture<BlockEvent.TransactionEvent> completableFuture = null;

        LifecycleApproveChaincodeDefinitionForMyOrgRequest lifecycleApproveChaincodeDefinitionForMyOrgRequest =
                hfClient.newLifecycleApproveChaincodeDefinitionForMyOrgRequest();
        try {
            lifecycleApproveChaincodeDefinitionForMyOrgRequest.setSequence(sequence);
            lifecycleApproveChaincodeDefinitionForMyOrgRequest.setChaincodeName(chaincodeName);
            lifecycleApproveChaincodeDefinitionForMyOrgRequest.setChaincodeVersion(chaincodeVersion);
            lifecycleApproveChaincodeDefinitionForMyOrgRequest.setInitRequired(initRequired);

            if (null != chaincodeCollectionConfiguration) {
                lifecycleApproveChaincodeDefinitionForMyOrgRequest.setChaincodeCollectionConfiguration(chaincodeCollectionConfiguration);
            }
            if (null != chaincodeEndorsementPolicy) {
                lifecycleApproveChaincodeDefinitionForMyOrgRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);
            }

            lifecycleApproveChaincodeDefinitionForMyOrgRequest.setPackageId(ChaincodePackageID);

            Collection<LifecycleApproveChaincodeDefinitionForMyOrgProposalResponse> responses
                    = channel.sendLifecycleApproveChaincodeDefinitionForMyOrgProposal(lifecycleApproveChaincodeDefinitionForMyOrgRequest, channel.getPeers());

            for (LifecycleApproveChaincodeDefinitionForMyOrgProposalResponse response : responses) {
                final Peer peer = response.getPeer();
                ChaincodeResponse.Status status = response.getStatus();
                boolean verified = response.isVerified();
                boolean invalid = response.isInvalid();
                if( ! status.equals("SUCCESS") ){
                    logger.info("审批链码失败");
                }
            }
           completableFuture = channel.sendTransaction(responses);
        } catch (InvalidArgumentException e) {
            e.printStackTrace();
        } catch (ProposalException e) {
            e.printStackTrace();
        }
        return completableFuture;
    }

    @Override
    public CompletableFuture<BlockEvent.TransactionEvent> commitChaincodeDefinitionRequest(
            long definitionSequence,
            String chaincodeName,
            String chaincodeVersion,
            LifecycleChaincodeEndorsementPolicy chaincodeEndorsementPolicy,
            ChaincodeCollectionConfiguration chaincodeCollectionConfiguration,
            boolean initRequired,
            Collection<Peer> endorsingPeers)
    {
        CompletableFuture<BlockEvent.TransactionEvent> transactionEventCompletableFuture = null;
        LifecycleCommitChaincodeDefinitionRequest lifecycleCommitChaincodeDefinitionRequest
                = hfClient.newLifecycleCommitChaincodeDefinitionRequest();

        try {
            lifecycleCommitChaincodeDefinitionRequest.setSequence(definitionSequence);
            lifecycleCommitChaincodeDefinitionRequest.setChaincodeName(chaincodeName);
            lifecycleCommitChaincodeDefinitionRequest.setChaincodeVersion(chaincodeVersion);
            if (null != chaincodeEndorsementPolicy) {
                lifecycleCommitChaincodeDefinitionRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);
            }
            if (null != chaincodeCollectionConfiguration) {
                lifecycleCommitChaincodeDefinitionRequest.setChaincodeCollectionConfiguration(chaincodeCollectionConfiguration);
            }
            lifecycleCommitChaincodeDefinitionRequest.setInitRequired(initRequired);

            Collection<LifecycleCommitChaincodeDefinitionProposalResponse> responses =
                    channel.sendLifecycleCommitChaincodeDefinitionProposal(lifecycleCommitChaincodeDefinitionRequest, endorsingPeers);

            for (LifecycleCommitChaincodeDefinitionProposalResponse resp : responses) {

                final Peer peer = resp.getPeer();
                ChaincodeResponse.Status status = resp.getStatus();
                boolean verified = resp.isVerified();
                if( ! status.equals("SUCCESS") ){
                    logger.info("提交链码失败");
                }

            }
            transactionEventCompletableFuture = channel.sendTransaction(responses);

        } catch (InvalidArgumentException e) {
            e.printStackTrace();
        } catch (ProposalException e) {
            e.printStackTrace();
        }
        return transactionEventCompletableFuture;
    }

    /**
     * 查询链码请求对象（需调用方自己发送此请求对象，并解析响应对象）
     *
     * @param
     * @return
     */
    @Override
    public LifecycleQueryInstalledChaincodesRequest createLifecycleQueryInstalledChaincodesRequest()
    {
        LifecycleQueryInstalledChaincodesRequest   lifecycleQueryInstalledChaincodesRequest = hfClient.newLifecycleQueryInstalledChaincodesRequest();
        return lifecycleQueryInstalledChaincodesRequest;
    }

    /**
     * 查询全部链码（直接返回所有链码名字）
     *
     * @param
     * @return
     */
    public Map<String, Object> listChaincodesOfPeerInstalled(String channelName,SmChain smChain
    ) {
        //调用构造函数，获取请求对象。
        LifecycleQueryInstalledChaincodesRequest lifecycleQueryInstalledChaincodesRequest = smChain.getChaincode().createLifecycleQueryInstalledChaincodesRequest();
        Collection<Peer> peers = hfClient.getChannel(channelName).getPeers();
        HashMap<String, Object> result = new HashMap<>();
        try {
            //拿到响应对象
            Collection<LifecycleQueryInstalledChaincodesProposalResponse> responses = hfClient.sendLifecycleQueryInstalledChaincodes(lifecycleQueryInstalledChaincodesRequest,peers);
            Collection<ProposalResponse> successful = new LinkedList<>();
            Collection<ProposalResponse> failed = new LinkedList<>();
            //一个response就是一个peer的信息
            for (LifecycleQueryInstalledChaincodesProposalResponse response : responses) {
                if (response.getStatus() == ProposalResponse.Status.SUCCESS) {
                    successful.add(response);
                    Collection<LifecycleQueryInstalledChaincodesProposalResponse.LifecycleQueryInstalledChaincodesResult> lifecycleQueryInstalledChaincodesResults = response.getLifecycleQueryInstalledChaincodesResult();
                    ArrayList<String> chainCodeNames = new ArrayList<>();
                    //一个lifecycleQueryInstalledChaincodesResult就是一个peer拥有的全部链码
                    for (LifecycleQueryInstalledChaincodesProposalResponse.LifecycleQueryInstalledChaincodesResult lifecycleQueryInstalledChaincodesResult: lifecycleQueryInstalledChaincodesResults) {
                        chainCodeNames.add(lifecycleQueryInstalledChaincodesResult.getLabel());
                    }
                    result.put(response.getPeer().getName(),chainCodeNames);

                } else {
                    failed.add(response);
                }
            }
            if (failed.size() > 0) {
                ProposalResponse first = failed.iterator().next();
                logger.info("Not enough endorsers for install :" + successful.size() + ".  " + first.getMessage());
            }
        } catch (org.hyperledger.fabric.sdk.exception.InvalidArgumentException e) {
            e.printStackTrace();
        } catch (ProposalException e) {
            e.printStackTrace();
        }
        return result;
    }
}
